/*
 * Copyright (c) 2002, 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.orb;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.AccessController;
import java.util.Collection;
import java.util.Iterator;

import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.portable.ValueFactory;

import com.sun.corba.se.pept.protocol.ClientRequestDispatcher;
import com.sun.corba.se.pept.transport.Acceptor;

import com.sun.corba.se.spi.activation.Locator;
import com.sun.corba.se.spi.activation.Activator;
import com.sun.corba.se.spi.activation.LocatorHelper;
import com.sun.corba.se.spi.activation.ActivatorHelper;
import com.sun.corba.se.spi.activation.EndPointInfo;

import com.sun.corba.se.spi.copyobject.ObjectCopierFactory;
import com.sun.corba.se.spi.copyobject.CopyobjectDefaults;
import com.sun.corba.se.spi.copyobject.CopierManager;

import com.sun.corba.se.spi.ior.IdentifiableFactoryFinder;
import com.sun.corba.se.spi.ior.IOR;
import com.sun.corba.se.spi.ior.IORFactories;

import com.sun.corba.se.spi.ior.iiop.IIOPFactories;

import com.sun.corba.se.spi.legacy.connection.ORBSocketFactory;

import com.sun.corba.se.spi.logging.CORBALogDomains;

import com.sun.corba.se.spi.oa.OADefault;
import com.sun.corba.se.spi.oa.ObjectAdapter;
import com.sun.corba.se.spi.oa.ObjectAdapterFactory;

import com.sun.corba.se.spi.orb.Operation;
import com.sun.corba.se.spi.orb.OperationFactory;
import com.sun.corba.se.spi.orb.ORBData;
import com.sun.corba.se.spi.orb.DataCollector;
import com.sun.corba.se.spi.orb.ORBConfigurator;
import com.sun.corba.se.spi.orb.ParserImplBase;
import com.sun.corba.se.spi.orb.PropertyParser;
import com.sun.corba.se.spi.orb.ORB;

import com.sun.corba.se.spi.orbutil.closure.Closure;
import com.sun.corba.se.spi.orbutil.closure.ClosureFactory;

import com.sun.corba.se.spi.protocol.RequestDispatcherRegistry;
import com.sun.corba.se.spi.protocol.CorbaServerRequestDispatcher;
import com.sun.corba.se.spi.protocol.RequestDispatcherDefault;
import com.sun.corba.se.spi.protocol.LocalClientRequestDispatcherFactory;

import com.sun.corba.se.spi.resolver.LocalResolver;
import com.sun.corba.se.spi.resolver.Resolver;
import com.sun.corba.se.spi.resolver.ResolverDefault;

import com.sun.corba.se.spi.transport.CorbaContactInfoList;
import com.sun.corba.se.spi.transport.CorbaContactInfoListFactory;
import com.sun.corba.se.spi.transport.SocketInfo;
import com.sun.corba.se.spi.transport.TransportDefault;

import com.sun.corba.se.spi.presentation.rmi.PresentationManager;
import com.sun.corba.se.spi.presentation.rmi.PresentationDefaults;

import com.sun.corba.se.spi.servicecontext.ServiceContext;
import com.sun.corba.se.spi.servicecontext.ServiceContextRegistry;
import com.sun.corba.se.spi.servicecontext.UEInfoServiceContext;
import com.sun.corba.se.spi.servicecontext.CodeSetServiceContext;
import com.sun.corba.se.spi.servicecontext.SendingContextServiceContext;
import com.sun.corba.se.spi.servicecontext.ORBVersionServiceContext;
import com.sun.corba.se.spi.servicecontext.MaxStreamFormatVersionServiceContext;
import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.transport.SocketOrChannelAcceptorImpl;

// XXX This should go away once we get rid of the port exchange for ORBD
import com.sun.corba.se.spi.legacy.connection.LegacyServerSocketEndPointInfo;
import com.sun.corba.se.impl.legacy.connection.SocketFactoryAcceptorImpl;
import com.sun.corba.se.impl.legacy.connection.SocketFactoryContactInfoListImpl;
import com.sun.corba.se.impl.legacy.connection.USLPort;

// XXX These should move to SPI
import com.sun.corba.se.impl.orbutil.ORBConstants;

// XXX This needs an SPI
import com.sun.corba.se.impl.dynamicany.DynAnyFactoryImpl;

public class ORBConfiguratorImpl implements ORBConfigurator {

  private ORBUtilSystemException wrapper;

  public static class ConfigParser extends ParserImplBase {

    public Class[] userConfigurators = null;

    public PropertyParser makeParser() {
      PropertyParser parser = new PropertyParser();
      Operation action = OperationFactory.compose(
          OperationFactory.suffixAction(),
          OperationFactory.classAction()
      );
      parser.addPrefix(ORBConstants.SUN_PREFIX + "ORBUserConfigurators",
          action, "userConfigurators", Class.class);
      return parser;
    }
  }

  public void configure(DataCollector collector, ORB orb) {
    ORB theOrb = orb;
    wrapper = ORBUtilSystemException.get(orb, CORBALogDomains.ORB_LIFECYCLE);

    initObjectCopiers(theOrb);
    initIORFinders(theOrb);

    theOrb.setClientDelegateFactory(
        // REVISIT: this should be ProtocolDefault.
        TransportDefault.makeClientDelegateFactory(theOrb));

    initializeTransport(theOrb);

    initializeNaming(theOrb);
    initServiceContextRegistry(theOrb);
    initRequestDispatcherRegistry(theOrb);
    registerInitialReferences(theOrb);

    persistentServerInitialization(theOrb);

    runUserConfigurators(collector, theOrb);
  }

  private void runUserConfigurators(DataCollector collector, ORB orb) {
    // Run any pluggable configurators.  This is a lot like
    // ORBInitializers, only it uses the internal ORB and has
    // access to all data for parsing.
    ConfigParser parser = new ConfigParser();
    parser.init(collector);
    if (parser.userConfigurators != null) {
      for (int ctr = 0; ctr < parser.userConfigurators.length; ctr++) {
        Class cls = parser.userConfigurators[ctr];
        try {
          ORBConfigurator config = (ORBConfigurator) (cls.newInstance());
          config.configure(collector, orb);
        } catch (Exception exc) {
          // XXX Log this exception
          // ignore this for now: a bad user configurator does nothing
        }
      }
    }
  }

  private void persistentServerInitialization(ORB orb) {
    ORBData data = orb.getORBData();

    // determine the ORBD port so that persistent objrefs can be
    // created.
    if (data.getServerIsORBActivated()) {
      try {
        Locator locator = LocatorHelper.narrow(
            orb.resolve_initial_references(
                ORBConstants.SERVER_LOCATOR_NAME));
        Activator activator = ActivatorHelper.narrow(
            orb.resolve_initial_references(
                ORBConstants.SERVER_ACTIVATOR_NAME));
        Collection serverEndpoints =
            orb.getCorbaTransportManager().getAcceptors(null, null);
        EndPointInfo[] endpointList =
            new EndPointInfo[serverEndpoints.size()];
        Iterator iterator = serverEndpoints.iterator();
        int i = 0;
        while (iterator.hasNext()) {
          Object n = iterator.next();
          if (!(n instanceof LegacyServerSocketEndPointInfo)) {
            continue;
          }
          LegacyServerSocketEndPointInfo ep =
              (LegacyServerSocketEndPointInfo) n;
          // REVISIT - use exception instead of -1.
          int port = locator.getEndpoint(ep.getType());
          if (port == -1) {
            port = locator.getEndpoint(SocketInfo.IIOP_CLEAR_TEXT);
            if (port == -1) {
              throw new Exception(
                  "ORBD must support IIOP_CLEAR_TEXT");
            }
          }

          ep.setLocatorPort(port);

          endpointList[i++] =
              new EndPointInfo(ep.getType(), ep.getPort());
        }

        activator.registerEndpoints(
            data.getPersistentServerId(), data.getORBId(),
            endpointList);
      } catch (Exception ex) {
        throw wrapper.persistentServerInitError(
            CompletionStatus.COMPLETED_MAYBE, ex);
      }
    }
  }

  /**
   * This is made somewhat complex because we are currently supporting
   * the ContactInfoList/Acceptor *AND* the legacy SocketFactory
   * transport architecture.
   */
  private void initializeTransport(final ORB orb) {
    ORBData od = orb.getORBData();

    CorbaContactInfoListFactory contactInfoListFactory =
        od.getCorbaContactInfoListFactory();
    Acceptor[] acceptors = od.getAcceptors();

    // BEGIN Legacy
    ORBSocketFactory legacySocketFactory = od.getLegacySocketFactory();
    USLPort[] uslPorts = od.getUserSpecifiedListenPorts();
    setLegacySocketFactoryORB(orb, legacySocketFactory);
    // END Legacy

    //
    // Check for incorrect configuration.
    //

    if (legacySocketFactory != null && contactInfoListFactory != null) {
      throw wrapper.socketFactoryAndContactInfoListAtSameTime();
    }

    if (acceptors.length != 0 && legacySocketFactory != null) {
      throw wrapper.acceptorsAndLegacySocketFactoryAtSameTime();
    }

    //
    // Client and Server side setup.
    //

    od.getSocketFactory().setORB(orb);

    //
    // Set up client side.
    //

    if (legacySocketFactory != null) {
      // BEGIN Legacy
      // Since the user specified a legacy socket factory we need to
      // use a ContactInfoList that will use the legacy socket factory.
      contactInfoListFactory =
          new CorbaContactInfoListFactory() {
            public void setORB(ORB orb) {
            }

            public CorbaContactInfoList create(IOR ior) {
              return new SocketFactoryContactInfoListImpl(
                  orb, ior);
            }
          };
      // END Legacy
    } else if (contactInfoListFactory != null) {
      // The user specified an explicit ContactInfoListFactory.
      contactInfoListFactory.setORB(orb);
    } else {
      // Use the default.
      contactInfoListFactory =
          TransportDefault.makeCorbaContactInfoListFactory(orb);
    }
    orb.setCorbaContactInfoListFactory(contactInfoListFactory);

    //
    // Set up server side.
    //

    //
    // Maybe allocate the Legacy default listener.
    //
    // If old legacy properties set, or there are no explicit
    // acceptors then register a default listener.  Type of
    // default listener depends on presence of legacy socket factory.
    //
    // Note: this must happen *BEFORE* registering explicit acceptors.
    //

    // BEGIN Legacy
    int port = -1;
    if (od.getORBServerPort() != 0) {
      port = od.getORBServerPort();
    } else if (od.getPersistentPortInitialized()) {
      port = od.getPersistentServerPort();
    } else if (acceptors.length == 0) {
      port = 0;
    }
    if (port != -1) {
      createAndRegisterAcceptor(orb, legacySocketFactory, port,
          LegacyServerSocketEndPointInfo.DEFAULT_ENDPOINT,
          SocketInfo.IIOP_CLEAR_TEXT);
    }
    // END Legacy

    for (int i = 0; i < acceptors.length; i++) {
      orb.getCorbaTransportManager().registerAcceptor(acceptors[i]);
    }

    // BEGIN Legacy
    // Allocate user listeners.
    USLPort[] ports = od.getUserSpecifiedListenPorts();
    if (ports != null) {
      for (int i = 0; i < ports.length; i++) {
        createAndRegisterAcceptor(
            orb, legacySocketFactory, ports[i].getPort(),
            LegacyServerSocketEndPointInfo.NO_NAME,
            ports[i].getType());
      }
    }
    // END Legacy
  }

  /*
   * Legacy: name.
   */
  // REVISIT: see ORBD. make factory in TransportDefault.
  private void createAndRegisterAcceptor(ORB orb,
      ORBSocketFactory legacySocketFactory,
      int port, String name, String type) {
    Acceptor acceptor;
    if (legacySocketFactory == null) {
      acceptor =
          new SocketOrChannelAcceptorImpl(orb, port, name, type);
    } else {
      acceptor =
          new SocketFactoryAcceptorImpl(orb, port, name, type);
    }
    orb.getTransportManager().registerAcceptor(acceptor);
  }

  private void setLegacySocketFactoryORB(
      final ORB orb, final ORBSocketFactory legacySocketFactory) {
    if (legacySocketFactory == null) {
      return;
    }

    // Note: the createServerSocket and createSocket methods on the
    // DefaultSocketFactory need to get data from the ORB but
    // we cannot change the interface.  So set the ORB (if it's ours)
    // by reflection.

    try {
      AccessController.doPrivileged(
          new PrivilegedExceptionAction() {
            public Object run()
                throws InstantiationException, IllegalAccessException {
              try {
                Class[] params = {ORB.class};
                Method method =
                    legacySocketFactory.getClass().getMethod(
                        "setORB", params);
                Object[] args = {orb};
                method.invoke(legacySocketFactory, args);
              } catch (NoSuchMethodException e) {
                // NOTE: If there is no method then it
                // is not ours - so ignore it.
                ;
              } catch (IllegalAccessException e) {
                RuntimeException rte = new RuntimeException();
                rte.initCause(e);
                throw rte;
              } catch (InvocationTargetException e) {
                RuntimeException rte = new RuntimeException();
                rte.initCause(e);
                throw rte;
              }
              return null;
            }
          }
      );
    } catch (Throwable t) {
      throw wrapper.unableToSetSocketFactoryOrb(t);
    }
  }

  private void initializeNaming(ORB orb) {
    LocalResolver localResolver = ResolverDefault.makeLocalResolver();
    orb.setLocalResolver(localResolver);

    Resolver bootResolver = ResolverDefault.makeBootstrapResolver(orb,
        orb.getORBData().getORBInitialHost(),
        orb.getORBData().getORBInitialPort());

    Operation urlOperation = ResolverDefault.makeINSURLOperation(orb,
        bootResolver);
    orb.setURLOperation(urlOperation);

    Resolver irResolver = ResolverDefault.makeORBInitRefResolver(urlOperation,
        orb.getORBData().getORBInitialReferences());

    Resolver dirResolver = ResolverDefault.makeORBDefaultInitRefResolver(
        urlOperation, orb.getORBData().getORBDefaultInitialReference());

    Resolver resolver =
        ResolverDefault.makeCompositeResolver(localResolver,
            ResolverDefault.makeCompositeResolver(irResolver,
                ResolverDefault.makeCompositeResolver(dirResolver,
                    bootResolver)));
    orb.setResolver(resolver);
  }

  private void initServiceContextRegistry(ORB orb) {
    ServiceContextRegistry scr = orb.getServiceContextRegistry();

    scr.register(UEInfoServiceContext.class);
    scr.register(CodeSetServiceContext.class);
    scr.register(SendingContextServiceContext.class);
    scr.register(ORBVersionServiceContext.class);
    scr.register(MaxStreamFormatVersionServiceContext.class);
  }

  private void registerInitialReferences(final ORB orb) {
    // Register the Dynamic Any factory
    Closure closure = new Closure() {
      public java.lang.Object evaluate() {
        return new DynAnyFactoryImpl(orb);
      }
    };

    Closure future = ClosureFactory.makeFuture(closure);
    orb.getLocalResolver().register(ORBConstants.DYN_ANY_FACTORY_NAME,
        future);
  }

  private static final int ORB_STREAM = 0;

  private void initObjectCopiers(ORB orb) {
    // No optimization or policy selection here.
    ObjectCopierFactory orbStream =
        CopyobjectDefaults.makeORBStreamObjectCopierFactory(orb);

    CopierManager cm = orb.getCopierManager();
    cm.setDefaultId(ORB_STREAM);

    cm.registerObjectCopierFactory(orbStream, ORB_STREAM);
  }

  private void initIORFinders(ORB orb) {
    IdentifiableFactoryFinder profFinder =
        orb.getTaggedProfileFactoryFinder();
    profFinder.registerFactory(IIOPFactories.makeIIOPProfileFactory());

    IdentifiableFactoryFinder profTempFinder =
        orb.getTaggedProfileTemplateFactoryFinder();
    profTempFinder.registerFactory(
        IIOPFactories.makeIIOPProfileTemplateFactory());

    IdentifiableFactoryFinder compFinder =
        orb.getTaggedComponentFactoryFinder();
    compFinder.registerFactory(
        IIOPFactories.makeCodeSetsComponentFactory());
    compFinder.registerFactory(
        IIOPFactories.makeJavaCodebaseComponentFactory());
    compFinder.registerFactory(
        IIOPFactories.makeORBTypeComponentFactory());
    compFinder.registerFactory(
        IIOPFactories.makeMaxStreamFormatVersionComponentFactory());
    compFinder.registerFactory(
        IIOPFactories.makeAlternateIIOPAddressComponentFactory());
    compFinder.registerFactory(
        IIOPFactories.makeRequestPartitioningComponentFactory());
    compFinder.registerFactory(
        IIOPFactories.makeJavaSerializationComponentFactory());

    // Register the ValueFactory instances for ORT
    IORFactories.registerValueFactories(orb);

    // Register an ObjectKeyFactory
    orb.setObjectKeyFactory(IORFactories.makeObjectKeyFactory(orb));
  }

  private void initRequestDispatcherRegistry(ORB orb) {
    RequestDispatcherRegistry scr = orb.getRequestDispatcherRegistry();

    // register client subcontracts
    ClientRequestDispatcher csub =
        RequestDispatcherDefault.makeClientRequestDispatcher();
    scr.registerClientRequestDispatcher(csub,
        ORBConstants.TOA_SCID);
    scr.registerClientRequestDispatcher(csub,
        ORBConstants.TRANSIENT_SCID);
    scr.registerClientRequestDispatcher(csub,
        ORBConstants.PERSISTENT_SCID);
    scr.registerClientRequestDispatcher(csub,
        ORBConstants.SC_TRANSIENT_SCID);
    scr.registerClientRequestDispatcher(csub,
        ORBConstants.SC_PERSISTENT_SCID);
    scr.registerClientRequestDispatcher(csub,
        ORBConstants.IISC_TRANSIENT_SCID);
    scr.registerClientRequestDispatcher(csub,
        ORBConstants.IISC_PERSISTENT_SCID);
    scr.registerClientRequestDispatcher(csub,
        ORBConstants.MINSC_TRANSIENT_SCID);
    scr.registerClientRequestDispatcher(csub,
        ORBConstants.MINSC_PERSISTENT_SCID);

    // register server delegates
    CorbaServerRequestDispatcher sd =
        RequestDispatcherDefault.makeServerRequestDispatcher(orb);
    scr.registerServerRequestDispatcher(sd,
        ORBConstants.TOA_SCID);
    scr.registerServerRequestDispatcher(sd,
        ORBConstants.TRANSIENT_SCID);
    scr.registerServerRequestDispatcher(sd,
        ORBConstants.PERSISTENT_SCID);
    scr.registerServerRequestDispatcher(sd,
        ORBConstants.SC_TRANSIENT_SCID);
    scr.registerServerRequestDispatcher(sd,
        ORBConstants.SC_PERSISTENT_SCID);
    scr.registerServerRequestDispatcher(sd,
        ORBConstants.IISC_TRANSIENT_SCID);
    scr.registerServerRequestDispatcher(sd,
        ORBConstants.IISC_PERSISTENT_SCID);
    scr.registerServerRequestDispatcher(sd,
        ORBConstants.MINSC_TRANSIENT_SCID);
    scr.registerServerRequestDispatcher(sd,
        ORBConstants.MINSC_PERSISTENT_SCID);

    orb.setINSDelegate(
        RequestDispatcherDefault.makeINSServerRequestDispatcher(orb));

    // register local client subcontracts
    LocalClientRequestDispatcherFactory lcsf =
        RequestDispatcherDefault.makeJIDLLocalClientRequestDispatcherFactory(
            orb);
    scr.registerLocalClientRequestDispatcherFactory(lcsf,
        ORBConstants.TOA_SCID);

    lcsf =
        RequestDispatcherDefault.makePOALocalClientRequestDispatcherFactory(
            orb);
    scr.registerLocalClientRequestDispatcherFactory(lcsf,
        ORBConstants.TRANSIENT_SCID);
    scr.registerLocalClientRequestDispatcherFactory(lcsf,
        ORBConstants.PERSISTENT_SCID);

    lcsf = RequestDispatcherDefault.
        makeFullServantCacheLocalClientRequestDispatcherFactory(orb);
    scr.registerLocalClientRequestDispatcherFactory(lcsf,
        ORBConstants.SC_TRANSIENT_SCID);
    scr.registerLocalClientRequestDispatcherFactory(lcsf,
        ORBConstants.SC_PERSISTENT_SCID);

    lcsf = RequestDispatcherDefault.
        makeInfoOnlyServantCacheLocalClientRequestDispatcherFactory(orb);
    scr.registerLocalClientRequestDispatcherFactory(lcsf,
        ORBConstants.IISC_TRANSIENT_SCID);
    scr.registerLocalClientRequestDispatcherFactory(lcsf,
        ORBConstants.IISC_PERSISTENT_SCID);

    lcsf = RequestDispatcherDefault.
        makeMinimalServantCacheLocalClientRequestDispatcherFactory(orb);
    scr.registerLocalClientRequestDispatcherFactory(lcsf,
        ORBConstants.MINSC_TRANSIENT_SCID);
    scr.registerLocalClientRequestDispatcherFactory(lcsf,
        ORBConstants.MINSC_PERSISTENT_SCID);

        /* Register the server delegate that implements the ancient bootstrap
         * naming protocol.  This takes an object key of either "INIT" or
         * "TINI" to allow for big or little endian implementations.
         */
    CorbaServerRequestDispatcher bootsd =
        RequestDispatcherDefault.makeBootstrapServerRequestDispatcher(
            orb);
    scr.registerServerRequestDispatcher(bootsd, "INIT");
    scr.registerServerRequestDispatcher(bootsd, "TINI");

    // Register object adapter factories
    ObjectAdapterFactory oaf = OADefault.makeTOAFactory(orb);
    scr.registerObjectAdapterFactory(oaf, ORBConstants.TOA_SCID);

    oaf = OADefault.makePOAFactory(orb);
    scr.registerObjectAdapterFactory(oaf, ORBConstants.TRANSIENT_SCID);
    scr.registerObjectAdapterFactory(oaf, ORBConstants.PERSISTENT_SCID);
    scr.registerObjectAdapterFactory(oaf, ORBConstants.SC_TRANSIENT_SCID);
    scr.registerObjectAdapterFactory(oaf, ORBConstants.SC_PERSISTENT_SCID);
    scr.registerObjectAdapterFactory(oaf, ORBConstants.IISC_TRANSIENT_SCID);
    scr.registerObjectAdapterFactory(oaf, ORBConstants.IISC_PERSISTENT_SCID);
    scr.registerObjectAdapterFactory(oaf, ORBConstants.MINSC_TRANSIENT_SCID);
    scr.registerObjectAdapterFactory(oaf, ORBConstants.MINSC_PERSISTENT_SCID);
  }
}

// End of file.
